package edu.ncsu.csc.ip.server;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.Arrays;
import java.util.Random;
import edu.ncsu.csc.ip.common.Log;
import edu.ncsu.csc.ip.server.protocol.ProtocolServerImpl;

/**
 * Class <code>RegistrationServer</code>
 * 
 * 
 * 
 * @author Moin Ayazifar <mayazif@ncsu.edu>
 * @author Steven Elliott <sdelliot@ncsu.edu>
 * 
 */

public class Receiver {
	
	public static void main(String[] args) throws IOException {
		Random randomGenerator = new Random();
		DatagramSocket serverSocket = null;		
		ProtocolServerImpl protocol = null;
		int port = 65423;
		double p = 0;
		int fileNameCount = 1;
		String filename = "out.txt";
		String originalFile = "";
		byte[] receiveData = new byte[1024];
		
		
		if(args.length != 3){
			Log.println("Proper Usage is: java receiver <Port#> <filename> <probability>");
	        System.exit(0);
	    }
		
		try {
			port = Integer.parseInt(args[0]);
		} catch (NumberFormatException nfe) {
			Log.println("Port is not a number: " + args[0]
					+ ". Using the default port:" + port);
		}
		
		filename = args[1];
		originalFile = filename;
		try {
			p = Double.parseDouble(args[2]);
			if(0 > p || p > 1){
				Log.println("Probability must be in the range [0,1): ");
				System.exit(0);
			}
		} catch (NumberFormatException nfe) {
			Log.println("Probability is not a number: " + args[2]);
			System.exit(-1);
		}
		
		try {
			serverSocket = new DatagramSocket(port);
			Log.println("Server is listening on port: " + port + " . . . ");
		} catch (IOException e) {
			Log.err("Could not listen on port: " + port
					+ ". Closing program ...");
			System.exit(-1);
		}

		long prevSeq = -1;
		long currSeq;
		while (true) {

			receiveData = new byte[1024];

			DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);

			serverSocket.receive(receivePacket);
			protocol = new ProtocolServerImpl(receivePacket);
			currSeq = protocol.getSeqNum(receivePacket.getData());
			
			double r = randomGenerator.nextDouble();
			if(r <= p){
				Log.println("Packet loss, sequence number = " + protocol.getSeqNum(receivePacket.getData()));
				continue;
			}
			
			if(currSeq == prevSeq){
				Log.println("Duplicate packet: " + currSeq);
				try {
					// Sleep and then resend the ACK
					Thread.sleep(randomGenerator.nextInt(300));
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				InetAddress IPAddress = receivePacket.getAddress();
				byte [] ACK = protocol.makeACK(Arrays.copyOfRange(receivePacket.getData(), 0, 4));
				DatagramPacket sendPacket = new DatagramPacket(ACK,
						ACK.length, IPAddress, receivePacket.getPort());
				serverSocket.send(sendPacket);
				
				continue;
			}
			prevSeq = currSeq;
			// Check the packet checksum
			if(protocol.isChecksumCorrect(receivePacket.getData())){
				// Check if it is the last packet
				if(protocol.parseMessage(receivePacket.getData()).equals("FFFF")){
					filename = originalFile.split("\\.")[0] + "_" + fileNameCount + "." + originalFile.split("\\.")[1];
					Log.println("FIN Packet, updating filename to = " + filename);
					fileNameCount++;
				}
					// Write data to file and send ACK
					 Thread t = new ReceiverThread(serverSocket, receivePacket, filename);
					 t.start();
					 
					 // Wait until the ACK is sent to listen again
					 try {
						t.join();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				
			// Ignore packet and continue listening
			} else{
				Log.println("Packet loss, sequence number = " + protocol.getSeqNum(receivePacket.getData()));
				continue;
			}	
		}
//		serverSocket.close();
	}
}
